package org.govhack.okcapital.controllers;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.govhack.okcapital.model.CityRating;
import org.govhack.okcapital.model.DatasetProfile;
import org.govhack.okcapital.model.DatasetRating;
import org.govhack.okcapital.services.CsvDataService;
import org.govhack.okcapital.services.CsvDataService.City;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller for managing city ratings
 * @author Josh Vote
 *
 */
@Controller
public class RatingController extends BaseController {
    
    @Autowired
    CsvDataService csvDataService;
    
    
    
    /**
     * Returns a 32x32 icon summarising a city's rating
     * @param cityId The city ID to rate
     * @param datasetIds The list of dataset ID's to be weighted in rating this city. Must map 1-1 with datasetWeightings
     * @param datasetWeightings The list of dataset weights for rating this city. Must map 1-1 with datasetIds
     * @throws IOException 
     */
    @RequestMapping("getCityRatingIcon.do")
    public void getCityRatingIcon(@RequestParam(value="cityId", required=true) String cityId,
            @RequestParam(value="datasetId", required=false) String[] datasetIds,
            @RequestParam(value="datasetWeighting", required=false) Integer[] datasetWeightings,
            HttpServletResponse response) throws IOException {
       
        //TODO: this is faked
        InputStream fakeIcon = this.getClass().getResourceAsStream("testIcon.png");
        
        //Pipe image to output
        response.setContentType("image/png");
        ServletOutputStream outputStream = response.getOutputStream();
        writeInputToOutputStream(fakeIcon, outputStream, 1024 * 1024, true);
        outputStream.close();
    }
    
    @RequestMapping("getCityRating.do")
    public ModelAndView getCityRating(@RequestParam(value="cityId", required=true) String cityId,
            @RequestParam(value="datasetId", required=false) String[] datasetIds,
            @RequestParam(value="datasetWeighting", required=false) Integer[] datasetWeightings) {
        
        Map<String, DatasetProfile> profiles = new HashMap<String, DatasetProfile>();
        if (datasetIds != null && datasetWeightings != null && datasetIds.length == datasetWeightings.length) {
            for (int i = 0; i < datasetIds.length; i++) {
                profiles.put(datasetIds[i], new DatasetProfile(datasetIds[i], datasetWeightings[i]));
            }
        }
        
        City city = null;
        if (cityId.contains("Adelaide")) {
            city = City.Adelaide;
        } else if (cityId.contains("Brisbane")) {
            city = City.Brisbane;
        }else if (cityId.contains("Canberra")) {
            city = City.Canberra;
        }else if (cityId.contains("Darwin")) {
            city = City.Darwin;
        }else if (cityId.contains("Hobart")) {
            city = City.Hobart;
        }else if (cityId.contains("Melbourne")) {
            city = City.Melbourne;
        }else if (cityId.contains("Perth")) {
            city = City.Perth;
        }else if (cityId.contains("Sydney")) {
            city = City.Sydney;
        }
        
        return generateJsonResponse(true, csvDataService.rateCity(city, profiles, false));
    }
    
    @RequestMapping("getCityRatingNormalised.do")
    public ModelAndView getCityRatingNormalised(@RequestParam(value="cityId", required=true) String cityId,
            @RequestParam(value="datasetId", required=false) String[] datasetIds,
            @RequestParam(value="datasetWeighting", required=false) Integer[] datasetWeightings) {
        
        Map<String, DatasetProfile> profiles = new HashMap<String, DatasetProfile>();
        if (datasetIds != null && datasetWeightings != null && datasetIds.length == datasetWeightings.length) {
            for (int i = 0; i < datasetIds.length; i++) {
                profiles.put(datasetIds[i], new DatasetProfile(datasetIds[i], datasetWeightings[i]));
            }
        }
        
        City city = null;
        if (cityId.contains("Adelaide")) {
            city = City.Adelaide;
        } else if (cityId.contains("Brisbane")) {
            city = City.Brisbane;
        }else if (cityId.contains("Canberra")) {
            city = City.Canberra;
        }else if (cityId.contains("Darwin")) {
            city = City.Darwin;
        }else if (cityId.contains("Hobart")) {
            city = City.Hobart;
        }else if (cityId.contains("Melbourne")) {
            city = City.Melbourne;
        }else if (cityId.contains("Perth")) {
            city = City.Perth;
        }else if (cityId.contains("Sydney")) {
            city = City.Sydney;
        }
        
        //Argghrhgrhgrh
        Map<City, Double> cityTotals = new HashMap<City, Double>();
        double min = Double.MAX_VALUE;
        double max = Double.MIN_NORMAL;
        for(City scannedCity : City.values()) {
            double total = totalRating(csvDataService.rateCity(scannedCity, profiles, true));
            cityTotals.put(scannedCity, total);
            
            if (total < min) {
                min = total;
            }
            if (total > max) {
                max = total;
            }
        }
        
        double denom = max - min;
        if (denom == 0) {
            denom = 1;
        }
        
        double totalRating = cityTotals.get(city).doubleValue();
        double normalisedRating = (totalRating - min) / denom;
               
        return generateJsonResponse(true, Arrays.asList(new CityRating(cityId, normalisedRating)));
    }
    
    private double totalRating(List<DatasetRating> list) {
        double total = 0;
        for (DatasetRating ds : list) {
            total  += ds.getRating();
        }
        return total;
    }
}
